package cn.iocoder.yudao.module.shop.info.dal.mysql;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.business.audit.enums.AuditStatusEnum;
import cn.iocoder.yudao.framework.business.basic.dao.query.CoreLambdaQueryWrapper;
import cn.iocoder.yudao.framework.business.basic.dao.query.CoreQueryWrapper;
import cn.iocoder.yudao.framework.business.basic.enums.SortTypeEnum;
import cn.iocoder.yudao.framework.business.eligibility.dao.EligibilityApplyMapper;
import cn.iocoder.yudao.framework.business.evaluation.enums.ScoreTypeEnum;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
import cn.iocoder.yudao.framework.common.exception.Assertions;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.region.RegionUtils;
import cn.iocoder.yudao.framework.region.bo.RegionNode;
import cn.iocoder.yudao.framework.region.enums.RegionDataSet;
import cn.iocoder.yudao.module.api.shop.info.enums.ShopOwnerTypeEnum;
import cn.iocoder.yudao.module.shop.admin.pojo.po.ShopUser;
import cn.iocoder.yudao.module.shop.info.dal.dataobject.ShopInfoDO;
import cn.iocoder.yudao.module.shop.info.vo.AdminShopInfoQueryDTO;
import cn.iocoder.yudao.module.shop.info.vo.AppShopInfoQueryDTO;
import cn.iocoder.yudao.module.shop.info.vo.ShopInfoQueryDTO;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.github.yulichang.toolkit.JoinWrappers;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 店铺信息 Mapper
 *
 * @author 山野羡民（1032694760@qq.com）
 */
@Mapper
public interface ShopInfoMapper extends EligibilityApplyMapper<ShopInfoDO> {

    default void wrapKeyword(QueryWrapper<ShopInfoDO> wrapper, String keyword) {
        if (StrUtil.isNotEmpty(keyword)) {
            wrapper.and(w -> w.like("detail_address", keyword)
                    .or().like("name", keyword)
                    .or().like("introduction", keyword));
        }
    }

    default void wrapScoreType(CoreQueryWrapper<ShopInfoDO> wrapper, Integer scoreType) {
        if (scoreType == null) {
            return;
        }
        switch (ScoreTypeEnum.valueOf(scoreType)) {
            case POSITIVE:
                wrapper.gt("score", 3);
                break;
            case NEUTER:
                wrapper.eq("score", 3);
                break;
            case NEGATIVE:
                wrapper.lt("score", 3);
                break;
            default:
                break;
        }
    }

    @Override
    default int selectAuditPassCount(Long userId, Integer userType) {
        if (Objects.equals(userType, UserTypeEnum.MEMBER.getValue())) {
            return EligibilityApplyMapper.super.selectAuditPassCount(userId, null);
        }
        List<ShopInfoDO> list = selectJoinList(ShopInfoDO.class, JoinWrappers.lambda(ShopInfoDO.class)
                .select(ShopInfoDO::getAuditStatus)
                .leftJoin(ShopUser.class, ShopUser::getShopId, ShopInfoDO::getId)
                .eq(ShopUser::getAdminUserId, userId)
                .ne(ShopInfoDO::getAuditStatus, AuditStatusEnum.PASS.getValue()));
        if (CollUtil.isEmpty(list)) {
            return 0;
        }
        return list.size();
    }

    default PageResult<ShopInfoDO> selectPage(Integer current, Integer size, ShopInfoQueryDTO reqVO) {
        CoreQueryWrapper<ShopInfoDO> wrapper = new CoreQueryWrapper<>();
        if (reqVO instanceof AdminShopInfoQueryDTO dto) {
            wrapper.eqIfPresent("type", dto.getType());
            wrapper.eqIfPresent("self_operated", dto.getSelfOperated());
            wrapper.eqIfPresent("recommend", dto.getRecommend());
            wrapper.eqIfPresent("status", dto.getStatus());
            wrapper.betweenIfPresent("enter_time", dto.getEnterTime());
            wrapper.betweenIfPresent("expire_time", dto.getExpireTime());
            wrapper.eqIfPresent("category_id", reqVO.getCategoryId());
        }
        if (reqVO instanceof AppShopInfoQueryDTO dto) {
            if (CollUtil.isNotEmpty(dto.getCategoryIds())) {
                if (dto.getCategoryIds().size() == 1) {
                    wrapper.eq("category_id", dto.getCategoryIds().iterator().next());
                } else {
                    wrapper.in("category_id", dto.getCategoryIds());
                }
            } else {
                wrapper.eqIfPresent("category_id", reqVO.getCategoryId());
            }
        }
        if (!(reqVO instanceof AdminShopInfoQueryDTO)) {
            wrapper.eq("enabled", CommonStatusEnum.ENABLE.getStatus());
        }
        List<RegionNode> countyNodes = RegionUtils.getRegionList(RegionDataSet.ONLY_LATEST, reqVO.getCityCode());
        if (CollUtil.isNotEmpty(countyNodes)) {
            List<String> countyCodes = new ArrayList<>();
            countyNodes.forEach(child -> countyCodes.add(child.getCode()));
            wrapper.inIfPresent("county_code", countyCodes);
        }
        wrapKeyword(wrapper, reqVO.getKeyword());
        wrapScoreType(wrapper, reqVO.getScoreType());
        wrapSort(wrapper, reqVO);
        return selectPage(new PageParam().setPageNo(current).setPageSize(size), wrapper);
    }

    private void wrapSort(CoreQueryWrapper<ShopInfoDO> wrapper, ShopInfoQueryDTO reqVO) {
        String sortType = reqVO.getSortType();
        boolean sortAsc = Boolean.TRUE.equals(reqVO.getSortAsc());
        if (sortType != null) {
            switch (SortTypeEnum.codeOf(sortType)) {
                case RANDOM:
                    wrapper.last(" ORDER BY RAND()");
                    break;
                case DISTANCE:
                    wrapLongitudeAndLatitude(wrapper, reqVO.getLongitude(), reqVO.getLatitude());
                    break;
                case ID:
                    if (sortAsc) {
                        wrapper.orderByAsc("id");
                    } else {
                        wrapper.orderByDesc("id");
                    }
                    break;
                case SCORE:
                    if (sortAsc) {
                        wrapper.orderByAsc("score");
                    } else {
                        wrapper.orderByDesc("score");
                    }
                    break;
                case RECOMMEND:
                    if (sortAsc) {
                        wrapper.orderByAsc("recommend");
                    } else {
                        wrapper.orderByDesc("recommend");
                    }
                    break;
                case BROWSE_COUNT:
                    if (sortAsc) {
                        wrapper.orderByAsc("browse_count");
                    } else {
                        wrapper.orderByDesc("browse_count");
                    }
                    break;
                case SALES_COUNT:
                    if (sortAsc) {
                        wrapper.orderByAsc("sales_count");
                    } else {
                        wrapper.orderByDesc("sales_count");
                    }
                    break;
                default:
                    wrapper.orderByAsc("enabled");
                    wrapper.orderByDesc("id");
                    break;
            }
        }
    }


    default void wrapLongitudeAndLatitude(QueryWrapper<ShopInfoDO> wrapper, Double longitude, Double latitude) {
        if (latitude != null && longitude != null) {
            String sqlSegment = "(6378.138 * 2 * ASIN(SQRT(POW(SIN((latitude * PI() / 180 - {1} * PI() / 180) / 2),2) + COS(latitude * PI() / 180) * COS({1} * PI() / 180) * POW(SIN((longitude * PI() / 180 - {0} * PI() / 180) / 2),2))) * 1000) AS distance";
            wrapper.apply(sqlSegment, longitude, latitude);
            wrapper.orderByAsc("distance");
        }
    }

    default List<ShopInfoDO> selectListByStatus(@Param("status") Integer status) {
        return selectList(new CoreLambdaQueryWrapper<ShopInfoDO>()
                .eqIfPresent(ShopInfoDO::getStatus, status)
                .orderByDesc(ShopInfoDO::getRecommend)
                .orderByDesc(ShopInfoDO::getId));
    }

    default List<ShopInfoDO> selectListByIdsAndStatus(@Param("ids") Collection<Long> ids, @Param("status") Integer status) {
        return selectList(new CoreLambdaQueryWrapper<ShopInfoDO>()
                .inIfPresent(ShopInfoDO::getId, ids)
                .eqIfPresent(ShopInfoDO::getStatus, status)
                .orderByDesc(ShopInfoDO::getRecommend)
                .orderByDesc(ShopInfoDO::getId));
    }

    default Set<Long> selectShopIdsByCategoryIds(Set<Long> categoryIds) {
        if (CollUtil.isEmpty(categoryIds)) {
            return new HashSet<>();
        }
        CoreLambdaQueryWrapper<ShopInfoDO> wrapper = new CoreLambdaQueryWrapper<>();
        wrapper.select(ShopInfoDO::getId);
        wrapper.in(ShopInfoDO::getCategoryId, categoryIds);
        wrapper.eqIfPresent(ShopInfoDO::getEnabled, CommonStatusEnum.ENABLE.getStatus());
        wrapper.orderByDesc(ShopInfoDO::getRecommend);
        wrapper.orderByDesc(ShopInfoDO::getId);
        List<Map<String, Object>> maps = selectMaps(wrapper);
        if (CollUtil.isEmpty(maps)) {
            return new HashSet<>();
        }
        return maps.stream().map(map -> MapUtil.getLong(map, "id", 0L))
                .collect(Collectors.toSet());
    }

    default List<ShopInfoDO> selectStoreSellerByIds(Set<Long> shopIds, String cityCode) {
        if (CollUtil.isEmpty(shopIds)) {
            return new ArrayList<>();
        }
        CoreLambdaQueryWrapper<ShopInfoDO> wrapper = new CoreLambdaQueryWrapper<>();
        wrapper.eq(ShopInfoDO::getType, ShopOwnerTypeEnum.STORE_SELLER.getValue());
        wrapper.in(ShopInfoDO::getId, shopIds);
        wrapper.eq(ShopInfoDO::getEnabled, CommonStatusEnum.ENABLE.getStatus());
        List<RegionNode> countyNodes = RegionUtils.getRegionList(RegionDataSet.ONLY_LATEST, cityCode);
        if (CollUtil.isNotEmpty(countyNodes)) {
            List<String> countyCodes = new ArrayList<>();
            countyNodes.forEach(child -> countyCodes.add(child.getCode()));
            wrapper.inIfPresent(ShopInfoDO::getCountyCode, countyCodes);
        }
        wrapper.orderByDesc(ShopInfoDO::getRecommend);
        wrapper.orderByDesc(ShopInfoDO::getId);
        return selectList(wrapper);
    }


    default List<ShopInfoDO> selectStoreSellerByIds(Set<Long> shopIds, Set<Long> categoryIds) {
        if (CollUtil.isEmpty(shopIds)) {
            return new ArrayList<>();
        }
        CoreLambdaQueryWrapper<ShopInfoDO> wrapper = new CoreLambdaQueryWrapper<>();
        wrapper.eq(ShopInfoDO::getType, ShopOwnerTypeEnum.STORE_SELLER.getValue());
        wrapper.in(ShopInfoDO::getId, shopIds);
        if (CollUtil.isNotEmpty(categoryIds)) {
            wrapper.inIfPresent(ShopInfoDO::getCategoryId, categoryIds);
        }
        wrapper.eq(ShopInfoDO::getEnabled, CommonStatusEnum.ENABLE.getStatus());
        wrapper.orderByDesc(ShopInfoDO::getRecommend);
        wrapper.orderByDesc(ShopInfoDO::getId);
        return selectList(wrapper);
    }

    default List<ShopInfoDO> selectListByKeyword(String keyword) {
        if (StrUtil.isBlank(keyword)) {
            return new ArrayList<>();
        }
        CoreLambdaQueryWrapper<ShopInfoDO> wrapper = new CoreLambdaQueryWrapper<>();
        if (NumberUtil.isNumber(keyword)) {
            Assertions.isTrue(keyword.length() >= 4, "需要手机尾号或完整的手机号码");
            wrapper.like(ShopInfoDO::getContactMobile, keyword);
        } else {
            wrapper.and(w -> w.like(ShopInfoDO::getName, keyword)
                    .or().like(ShopInfoDO::getContactName, keyword));
        }
        wrapper.limitN(300);
        return selectList(wrapper);
    }

    default Long selectIdByMemberUserId(Long memberUserId) {
        if (memberUserId == null || memberUserId <= 0L) {
            return null;
        }
        CoreLambdaQueryWrapper<ShopInfoDO> wrapper = new CoreLambdaQueryWrapper<>();
        wrapper.select(ShopInfoDO::getId);
        wrapper.eq(ShopInfoDO::getUserId, memberUserId);
        wrapper.limitN(1);
        ShopInfoDO aDo = selectOne(wrapper);
        if (aDo == null) {
            return null;
        }
        return aDo.getId();
    }

    default double selectServiceFeePercentById(Long id) {
        if (id == null || id <= 0L) {
            return 0D;
        }
        CoreLambdaQueryWrapper<ShopInfoDO> wrapper = new CoreLambdaQueryWrapper<>();
        wrapper.select(ShopInfoDO::getServiceFeePercent);
        wrapper.eq(ShopInfoDO::getId, id);
        wrapper.limitN(1);
        ShopInfoDO aDo = selectOne(wrapper);
        if (aDo == null) {
            return 100D;
        }
        Double percent = aDo.getServiceFeePercent();
        if (percent == null) {
            percent = 100D;
        }
        return percent;
    }

    default void updateSalesCount(Long shopId, int count) {
        if (shopId == null || shopId <= 0L || count == 0) {
            return;
        }
        LambdaUpdateWrapper<ShopInfoDO> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.setSql("sales_count = sales_count + " + count);
        updateWrapper.eq(ShopInfoDO::getId, shopId);
        if (count < 0) {
            updateWrapper.ge(ShopInfoDO::getSalesCount, -1 * count);
        }
        update(null, updateWrapper);
    }

    default List<Long> selectIdsByCountyCodes(List<String> countyCodes) {
        if (CollUtil.isEmpty(countyCodes)) {
            return List.of();
        }
        CoreLambdaQueryWrapper<ShopInfoDO> wrapper = new CoreLambdaQueryWrapper<>();
        wrapper.select(ShopInfoDO::getId);
        wrapper.in(ShopInfoDO::getCountyCode, countyCodes);
        List<ShopInfoDO> list = selectList(wrapper);
        if (CollUtil.isEmpty(list)) {
            return List.of();
        }
        return list.stream().map(ShopInfoDO::getId).collect(Collectors.toList());
    }

    default Set<Long> selectIdsByDisabled() {
        CoreLambdaQueryWrapper<ShopInfoDO> wrapper = new CoreLambdaQueryWrapper<>();
        wrapper.select(ShopInfoDO::getId);
        wrapper.eq(ShopInfoDO::getEnabled, CommonStatusEnum.DISABLE.getStatus());
        List<ShopInfoDO> list = selectList(wrapper);
        if (CollUtil.isEmpty(list)) {
            return Set.of();
        }
        return list.stream().map(ShopInfoDO::getId).collect(Collectors.toSet());
    }

}
